// File: CsoGatewayGlobalFunctions.js
// Version: 0.7.1.0
// Author: Pascal Dufresne
// Date: 2008-09-20
// Last update: 2009-05-25
// http://csogateway.codeplex.com
// http://csogateway.metaobjects.ca
// Copyright (C) 2010 Pascal Dufresne

/*
 * Global functions used in the CSO Gateway. These function are declared in the Global namespace and are visible everywhere.
 *
 * Global functions
 * ----------
 * AssertArgumentNotUndefined(String, String)
 * AssertArgumentNotNullOrUndefined(String, String)
 * AssertArgumentType(Object, Type, String)
 * AssertArgumentIsNumberInRange(Number, Number, Number, String)
 * ImportNamespace(String)
 * ImportType(Function)
 * BinarySearch(Array, Object, Boolean)
 * BinarySearchCustom(Array, Object, Boolean, Function)
 *
 */

/*
 * Throws an exception if the given value is underfined. Will not throw an exception if it is null.
 * arg:String - Any value.
 * argVarName:String - The name of the variable to which the value is assigned. To be used in the exception message.
 */
AssertArgumentNotUndefined = function(arg, argVarName)
{
	if(arg === undefined)
		throw Error.argumentUndefined(argVarName);
}

/*
 * Throws an exception if the given value is underfined or null.
 * arg:String - Any value.
 * argVarName:String - The name of the variable to which the value is assigned. To be used in the exception message.
 */
AssertArgumentNotNullOrUndefined = function(arg, argVarName)
{
	if(arg == undefined)
		throw Error.argumentNull(argVarName);
}

/*
 * Throws an exception if the given value is not an instance of the given type.
 * arg:String - Any value.
 * type:Function - The type for which the value is suppose to be an instance of.
 * argVarName:String - The name of the variable to which the value is assigned. To be used in the exception message.
 */
AssertArgumentType = function(arg, type, argVarName)
{
	AssertArgumentNotNullOrUndefined(arg, argVarName);

	if (!type.isInstanceOfType(arg))
		throw Error.argumentType(argVarName, Object.getType(arg), type)
}

/*
 * Throws an exception if the given Number is not between two other given Number values.
 * arg:Number - A Number value.
 * min:Number - The lower limit.
 * min:Number - The upper limit.
 * argVarName:String - The name of the variable to which the value is assigned. To be used in the exception message.
 */
AssertArgumentIsNumberInRange = function(arg, min, max, argVarName)
{
	AssertArgumentType(arg, Number, argVarName);
	
	if((arg < min) || (arg > max))
		throw new Error.argumentOutOfRange(argVarName, arg);
}

/*
 * Throws an exception if the given Number is not an Integer.
 * arg:Number - A Number value.
 * argVarName:String - The name of the variable to which the value is assigned. To be used in the exception message.
 */
AssertArgumentIsInteger = function(arg, argVarName)
{
	AssertArgumentType(arg, Number, argVarName);
	
	if(((arg>=0) && (Math.floor(arg) !== arg)) || ((arg<0) && (Math.ceil(arg) !== arg)))
		throw new Error.argument(argVarName, arg.toString() + ' is not an integer');
}

/*
 * Throws an exception if the given Number is not an integer or if it is outside the range where
 * integer Number value are precise (between -9007199254740992 and 9007199254740992). 
 * arg:Number - A Number value.
 * argVarName:String - The name of the variable to which the value is assigned. To be used in the exception message.
 */
AssertArgumentIsPreciseInteger = function(arg, argVarName)
{
	AssertArgumentIsInteger(arg, argVarName);
	
	if((arg < -9007199254740992) || (arg > 9007199254740992))
		throw new Error.argumentOutOfRange(argVarName, arg);
}


/*
 * ImportNamespace will lookup for each class, interface and enum in a given namespace
 * and for each of them, define in the global scope a variable named as the type name
 * and pointing to the actual class, interface or enum definition.
 * Example: ImportNamespace(CsoGateway.Collections) will evaluate the following
 * expression:
 *			Dictionary = CsoGateway.Collections.Dictionary
 * so that users of the Dictionary class do not have to use the fully qualified
 * name 'CsoGateway.Collections.Dictionary' when creating an object of that type,
 * just the short name of the class:
 *			Dictionary d = new Dictionary();
 *
 * ns:String - The name of a registered namespace.
*/

ImportNamespace = function(ns)
{
	AssertArgumentNotNullOrUndefined(ns, 'ns in ImportNamespace(ns)');
	
	for(var e in ns)
	{
		if ((ns[e]['__class'] == true) ||  (ns[e]['__interface'] == true) ||  (ns[e]['__enum'] == true))
		{
			var names = ns[e]['__typeName'].split('.');
			
			eval(names[names.length - 1] + ' = ' + ns[e]['__typeName']);
		}
	}	
}

/*
 * Does the same thing as ImportNamespace but, for one type only.
 *
 * aType:Function - One of the predefined core objects of javascript or a registered class, interface or enum.
 */
ImportType = function(aType)
{
	AssertArgumentNotNullOrUndefined(aType, 'aType in ImportType');
	
	if ((aType['__class'] == true) ||  (aType['__interface'] == true) ||  (aType['__enum'] == true))
	{
		var names = aType['__typeName'].split('.');
		eval(names[names.length - 1] + ' = ' + aType['__typeName']);
	}
	else
	{
		throw new Error('ImportType: The passed parameter is not a class nor an interface, or, it has not been registered.');
	}
}


/*
 * Searches in an array for a value using a the binary search algorithm. If the value is not found and the parameter 'forPos' is true,
 * the position at which the value should be inserted to keep the array sorted is returned. If the value is not found and 'forPos'
 * is false, -1 is returned.
 *
 * array:Array    - The Array object to search the value in
 * value          - A value to look for in the array.
 * forPos:Boolean - If true, when the value is not found, the function will return correct insertion index for the value.
 *                  If false, the function returns -1.
 *
 */

BinarySearch = function(array, value, forPos){
    var l = -1, h = array.length,  m;
    while(h - l > 1)
        if(array[m = h + l >> 1] < value) l = m;
        else h = m;
    return (array[h] != value ? (forPos ? h : -1) : h);
};


/*
 * Searches in an array for a value using a the binary search algorithm using a specified function to compare object in the array.
 * If the value is not found and the parameter 'forPos' is true, the position at which the value should be inserted to keep the array
 * sorted is returned. If the value is not found and 'forPos' is false, -1 is returned.
 *
 * array:Array              - The Array object to search the value in
 * value                    - A value to look for in the array.
 * forPos:Boolean           - If true, when the value is not found, the function will return correct insertion index for the value.
 *                            If false, the function returns -1.
 * comparer:Function        - The comparer function used to decide if one value is smaller or bigger that another one.
 *
 */

BinarySearchCustom = function(array, value, forPos, comparer){
    var l = -1, h = array.length,  m;
    while(h - l > 1)
        if(comparer(array[m = h + l >> 1],value) < 0) l = m;
        else h = m;
    return (array[h] != value ? (forPos ? h : -1) : h);
};

/*
 * A comparer function that uses the value returned by the toString() function to compare the given objects. 
 */
ToStringComparer = function(s1, s2)
{
	return ((s1.toString() < s2.toString()) ? -1 : ((s1.toString() > s2.toString()) ? 1 :  0))
	
}